#include "atom.h"

using namespace PBVP;

/**
* Bits which indicate whether or not an element symbol is valid.
*<p>
* If the high bit is set, then it is valid as a standalone char.
* otherwise, bits 0-25 say whether or not is valid when followed
* by the letters a-z.
*/
const int Atom::elementCharMasks[26] = {
	//   Ac Ag Al Am Ar As At Au
	1 << ('c' - 'a') |
	1 << ('g' - 'a') |
	1 << ('l' - 'a') |
	1 << ('m' - 'a') |
	1 << ('r' - 'a') |
	1 << ('s' - 'a') |
	1 << ('t' - 'a') |
	1 << ('u' - 'a'),
	// B Ba Be Bh Bi Bk Br
	1 << 31 |
	1 << ('a' - 'a') |
	1 << ('e' - 'a') |
	1 << ('h' - 'a') |
	1 << ('i' - 'a') |
	1 << ('k' - 'a') |
	1 << ('r' - 'a'),
	// C Ca Cd Ce Cf Cl Cm Co Cr Cs Cu
	1 << 31 |
	1 << ('a' - 'a') |
	1 << ('d' - 'a') |
	1 << ('e' - 'a') |
	1 << ('f' - 'a') |
	1 << ('l' - 'a') |
	1 << ('m' - 'a') |
	1 << ('o' - 'a') |
	1 << ('r' - 'a') |
	1 << ('s' - 'a') |
	1 << ('u' - 'a'),
	//  D Db Dy
	1 << 31 |
	1 << ('b' - 'a') |
	1 << ('y' - 'a'),
	//   Er Es Eu
	1 << ('r' - 'a') |
	1 << ('s' - 'a') |
	1 << ('u' - 'a'),
	// F Fe Fm Fr
	1 << 31 |
	1 << ('e' - 'a') |
	1 << ('m' - 'a') |
	1 << ('r' - 'a'),
	//   Ga Gd Ge
	1 << ('a' - 'a') |
	1 << ('d' - 'a') |
	1 << ('e' - 'a'),
	// H He Hf Hg Ho Hs
	1 << 31 |
	1 << ('e' - 'a') |
	1 << ('f' - 'a') |
	1 << ('g' - 'a') |
	1 << ('o' - 'a') |
	1 << ('s' - 'a'),
	// I In Ir
	1 << 31 |
	1 << ('n' - 'a') |
	1 << ('r' - 'a'),
	//j
	0,
	// K Kr
	1 << 31 |
	1 << ('r' - 'a'),
	//   La Li Lr Lu
	1 << ('a' - 'a') |
	1 << ('i' - 'a') |
	1 << ('r' - 'a') |
	1 << ('u' - 'a'),
	//   Md Mg Mn Mo Mt
	1 << ('d' - 'a') |
	1 << ('g' - 'a') |
	1 << ('n' - 'a') |
	1 << ('o' - 'a') |
	1 << ('t' - 'a'),
	// N Na Nb Nd Ne Ni No Np
	1 << 31 |
	1 << ('a' - 'a') |
	1 << ('b' - 'a') |
	1 << ('d' - 'a') |
	1 << ('e' - 'a') |
	1 << ('i' - 'a') |
	1 << ('o' - 'a') |
	1 << ('p' - 'a'),
	// O Os
	1 << 31 |
	1 << ('s' - 'a'),
	// P Pa Pb Pd Pm Po Pr Pt Pu
	1 << 31 |
	1 << ('a' - 'a') |
	1 << ('b' - 'a') |
	1 << ('d' - 'a') |
	1 << ('m' - 'a') |
	1 << ('o' - 'a') |
	1 << ('r' - 'a') |
	1 << ('t' - 'a') |
	1 << ('u' - 'a'),
	//q
	0,
	//   Ra Rb Re Rf Rh Rn Ru
	1 << ('a' - 'a') |
	1 << ('b' - 'a') |
	1 << ('e' - 'a') |
	1 << ('f' - 'a') |
	1 << ('h' - 'a') |
	1 << ('n' - 'a') |
	1 << ('u' - 'a'),
	// S Sb Sc Se Sg Si Sm Sn Sr
	1 << 31 |
	1 << ('b' - 'a') |
	1 << ('c' - 'a') |
	1 << ('e' - 'a') |
	1 << ('g' - 'a') |
	1 << ('i' - 'a') |
	1 << ('m' - 'a') |
	1 << ('n' - 'a') |
	1 << ('r' - 'a'),
	//  T Ta Tb Tc Te Th Ti Tl Tm
	1 << 31 |
	1 << ('a' - 'a') |
	1 << ('b' - 'a') |
	1 << ('c' - 'a') |
	1 << ('e' - 'a') |
	1 << ('h' - 'a') |
	1 << ('i' - 'a') |
	1 << ('l' - 'a') |
	1 << ('m' - 'a'),
	// U
	1 << 31,
	// V
	1 << 31,
	// W
	1 << 31,
	//   Xe Xx
	1 << ('e' - 'a') |
	1 << ('x' - 'a'), // don't know if I should have Xx here or not?
	// Y Yb
	1 << 31 |
	1 << ('b' - 'a'),
	//   Zn Zr
	1 << ('n' - 'a') |
	1 << ('r' - 'a')
};

Atom::Atom()
: name("XXXX"), residueName("XXX")
{
	chainID = '\0';
	onBackbone = false;
	atomIndex = 0;
	residueIndex = 0;
}

Atom::Atom(const QString& atomName, const QString& resName)
{
	chainID = '\0';
	onBackbone = false;
	residueIndex = 0;
	name = atomName;
	residueName = resName;
	atomIndex = lookupAtom(atomName, resName);
	radius = elementTable[atomIndex].radius;
	color = Color(elementTable[atomIndex].red, elementTable[atomIndex].green, elementTable[atomIndex].blue);
	solventEnlargedRadius = elementTable[atomIndex].solventEnlargedRadius;
}

//////////////////////////////////////////////////////////////////////////
// Static functions
//////////////////////////////////////////////////////////////////////////
bool Atom::isValidAtomSymbol(char c)
{
	return c >= 'A' && c <= 'Z' && elementCharMasks[c - 'A'] < 0;
}

bool Atom::isValidAtomSymbol2(char c1, char c2)
{
	if (c2 >= 'A' && c2 <= 'Z')	c2 += 'a' - 'A';
	if (c1 < 'A' || c1 > 'Z' || c2 < 'a' || c2 > 'z')
		return false;
	return ((elementCharMasks[c1 - 'A'] >> (c2 - 'a')) & 1) != 0;
}

bool Atom::isValidAtomSymbol(const QString &symbol)
{
	if (symbol.length() == 1)
	{
		return isValidAtomSymbol(symbol.toAscii().at(0));
	} else if (symbol.length() == 2) {
		return isValidAtomSymbol2(symbol.toAscii().at(0), symbol.toAscii().at(1));
	}
	return false;
}

int Atom::lookupAtom(const QString &atomName, const QString &resName)
{
	int n = 0;
	
	for(int i = 1; i < PBVP::MAX_BIOCHEM_ELEMENTS; i++) {
		if(elementTable[i].atomName == atomName && elementTable[i].residueName == resName) {
			n = i;
			break;
		}
	}
	return n;
}

